/*  
    EQ2Emulator:  Everquest II Server Emulator
    Copyright (C) 2007  EQ2EMulator Development Team (http://www.eq2emulator.net)

    This file is part of EQ2Emulator.

    EQ2Emulator is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    EQ2Emulator is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with EQ2Emulator.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "debug.h"
#include <stdio.h>
#include <string.h>
#include "opcodemgr.h"
//#include "debug.h"
#include "emu_opcodes.h"
#include "../common/Log.h"

#if defined(SHARED_OPCODES) && !defined(EQ2)
	#include "EMuShareMem.h"
	extern LoadEMuShareMemDLL EMuShareMemDLL;
#endif

#include <map>
#include <string>
using namespace std;


//#define DEBUG_TRANSLATE


OpcodeManager::OpcodeManager() {
	loaded = false;
}
bool OpcodeManager::LoadOpcodesMap(map<string, uint16>* eq, OpcodeSetStrategy *s){
	//do the mapping and store them in the shared memory array
	bool ret = true;
	EmuOpcode emu_op;
	map<string, uint16>::iterator res;
	//stupid enum wont let me ++ on it...


	for(emu_op = (EmuOpcode)(0); emu_op < _maxEmuOpcode; emu_op=(EmuOpcode)(emu_op+1)) {
		//get the name of this emu opcode
		const char *op_name = OpcodeNames[emu_op];
		if(op_name[0] == '\0') {
			break;
		}
		
		//find the opcode in the file
		res = eq->find(op_name);
		if(res == eq->end()) {
			LogWrite(OPCODE__WARNING, 1, "Opcode", "Opcode %s is missing from the opcodes table.", op_name);
			continue;	//continue to give them a list of all missing opcodes
		}
		
		//ship the mapping off to shared mem.
		s->Set(emu_op, res->second);
	}
	return ret;
}
bool OpcodeManager::LoadOpcodesFile(const char *filename, OpcodeSetStrategy *s) {
	FILE *opf = fopen(filename, "r");
	if(opf == NULL) {
		LogWrite(OPCODE__ERROR, 0, "Opcode", "Unable to open opcodes file '%s'. Thats bad.", filename);
		return(false);
	}
	
	map<string, uint16> eq;
	
	//load the opcode file into eq, could swap in a nice XML parser here
	char line[2048];
	int lineno = 0;
	uint16 curop;
	while(!feof(opf)) {
		lineno++;
		line[0] = '\0';	//for blank line at end of file
		if(fgets(line, sizeof(line), opf) == NULL)
			break;
		
		//ignore any line that dosent start with OP_
		if(line[0] != 'O' || line[1] != 'P' || line[2] != '_')
			continue;
		
		char *num = line+3;	//skip OP_
		//look for the = sign
		while(*num != '=' && *num != '\0') {
			num++;
		}
		//make sure we found =
		if(*num != '=') {
			LogWrite(OPCODE__ERROR, 0, "Opcode", "Malformed opcode line at %s:%d\n", filename, lineno);
			continue;
		}
		*num = '\0';	//null terminate the name
		num++;			//num should point to the opcode
		
		//read the opcode
		if(sscanf(num, "0x%hx", &curop) != 1) {
			LogWrite(OPCODE__ERROR, 0, "Opcode", "Malformed opcode at %s:%d\n", filename, lineno);
			continue;
		}
		
		//we have a name and our opcode... stick it in the map		
		eq[line] = curop;
	}
	fclose(opf);
	return LoadOpcodesMap(&eq, s);
}

//convenience routines
const char *OpcodeManager::EmuToName(const EmuOpcode emu_op) {
	return(OpcodeNames[emu_op]);
}

const char *OpcodeManager::EQToName(const uint16 eq_op) {
	//first must resolve the eq op to an emu op
	EmuOpcode emu_op = EQToEmu(eq_op);
	return(OpcodeNames[emu_op]);
}

EmuOpcode OpcodeManager::NameSearch(const char *name) {
	EmuOpcode emu_op;
	//stupid enum wont let me ++ on it...
	for(emu_op = (EmuOpcode)(0); emu_op < _maxEmuOpcode; emu_op=(EmuOpcode)(emu_op+1)) {
		//get the name of this emu opcode
		const char *op_name = OpcodeNames[emu_op];
		if(!strcasecmp(op_name, name)) {
			return(emu_op);
		}
	}
	return(OP_Unknown);
}

RegularOpcodeManager::RegularOpcodeManager()
: MutableOpcodeManager()
{
	emu_to_eq = NULL;
	eq_to_emu = NULL;
}

RegularOpcodeManager::~RegularOpcodeManager() {
	safe_delete_array(emu_to_eq);
	safe_delete_array(eq_to_emu);
}

bool RegularOpcodeManager::LoadOpcodes(map<string, uint16>* eq) {
	NormalMemStrategy s;
	s.it = this;
	MOpcodes.lock();
	
	loaded = true;
	eq_to_emu = new EmuOpcode[MAX_EQ_OPCODE];
	emu_to_eq = new uint16[_maxEmuOpcode];
	EQOpcodeCount = MAX_EQ_OPCODE;
	EmuOpcodeCount = _maxEmuOpcode;
	
	//dont need to set eq_to_emu cause every element should get a value
	memset(eq_to_emu, 0, sizeof(EmuOpcode)*MAX_EQ_OPCODE);
	
	bool ret = LoadOpcodesMap(eq, &s);
	MOpcodes.unlock();
	return ret;
}

bool RegularOpcodeManager::LoadOpcodes(const char *filename) {
	NormalMemStrategy s;
	s.it = this;
	MOpcodes.lock();
	
	loaded = true;
	eq_to_emu = new EmuOpcode[MAX_EQ_OPCODE];
	emu_to_eq = new uint16[_maxEmuOpcode];
	EQOpcodeCount = MAX_EQ_OPCODE;
	EmuOpcodeCount = _maxEmuOpcode;
	
	//dont need to set eq_to_emu cause every element should get a value
	memset(eq_to_emu, 0, sizeof(EmuOpcode)*MAX_EQ_OPCODE);
	
	bool ret = LoadOpcodesFile(filename, &s);
	MOpcodes.unlock();
	return ret;
}

bool RegularOpcodeManager::ReloadOpcodes(const char *filename) {
	if(!loaded)
		return(LoadOpcodes(filename));
	
	NormalMemStrategy s;
	s.it = this;
	MOpcodes.lock();
	
	memset(eq_to_emu, 0, sizeof(uint16)*MAX_EQ_OPCODE);
	
	bool ret = LoadOpcodesFile(filename, &s);
	
	MOpcodes.unlock();
	return(ret);
}


uint16 RegularOpcodeManager::EmuToEQ(const EmuOpcode emu_op) {
	//opcode is checked for validity in GetEQOpcode
	uint16 res;
	MOpcodes.lock();
	res = emu_to_eq[emu_op];
	MOpcodes.unlock();
#ifdef _DEBUG_TRANSLATE
	fprintf(stderr, "M Translate Emu %s (%d) to EQ 0x%.4x\n", OpcodeNames[emu_op], emu_op, res);
#endif
	return(res);
}

EmuOpcode RegularOpcodeManager::EQToEmu(const uint16 eq_op) {
	//opcode is checked for validity in GetEmuOpcode
//Disabled since current live EQ uses the entire uint16 bitspace for opcodes
//	if(eq_op > MAX_EQ_OPCODE)
//		return(OP_Unknown);
	EmuOpcode res;
	MOpcodes.lock();
	res = eq_to_emu[eq_op];
	MOpcodes.unlock();
#ifdef _DEBUG_TRANSLATE
	fprintf(stderr, "M Translate EQ 0x%.4x to Emu %s (%d)\n", eq_op, OpcodeNames[res], res);
#endif
	return(res);
}

void RegularOpcodeManager::SetOpcode(EmuOpcode emu_op, uint16 eq_op) {
	
	//clear out old mapping
	uint16 oldop = emu_to_eq[emu_op];
	if(oldop != 0)
		eq_to_emu[oldop] = OP_Unknown;
	
	//use our strategy, since we have it
	NormalMemStrategy s;
	s.it = this;
	s.Set(emu_op, eq_op);
}


void RegularOpcodeManager::NormalMemStrategy::Set(EmuOpcode emu_op, uint16 eq_op) {
	if(uint32(emu_op) >= it->EmuOpcodeCount || eq_op >= it->EQOpcodeCount)
		return;
	it->emu_to_eq[emu_op] = eq_op;
	it->eq_to_emu[eq_op] = emu_op;
}

NullOpcodeManager::NullOpcodeManager()
: MutableOpcodeManager() {
}

bool NullOpcodeManager::LoadOpcodes(map<string, uint16>* eq) {
	return(true);
}

bool NullOpcodeManager::LoadOpcodes(const char *filename) {
	return(true);
}

bool NullOpcodeManager::ReloadOpcodes(const char *filename) {
	return(true);
}
	
uint16 NullOpcodeManager::EmuToEQ(const EmuOpcode emu_op) {
	return(0);
}

EmuOpcode NullOpcodeManager::EQToEmu(const uint16 eq_op) {
	return(OP_Unknown);
}

EmptyOpcodeManager::EmptyOpcodeManager()
: MutableOpcodeManager() {
}

	
bool EmptyOpcodeManager::LoadOpcodes(const char *filename) {
	return(true);
}

bool EmptyOpcodeManager::LoadOpcodes(map<string, uint16>* eq) {
	return(true);
}

bool EmptyOpcodeManager::ReloadOpcodes(const char *filename) {
	return(true);
}
	
uint16 EmptyOpcodeManager::EmuToEQ(const EmuOpcode emu_op) {
	map<EmuOpcode, uint16>::iterator f;
	f = emu_to_eq.find(emu_op);
	return(f == emu_to_eq.end()? 0 : f->second);
}

EmuOpcode EmptyOpcodeManager::EQToEmu(const uint16 eq_op) {
	map<uint16, EmuOpcode>::iterator f;
	f = eq_to_emu.find(eq_op);
	return(f == eq_to_emu.end()?OP_Unknown:f->second);
}

void EmptyOpcodeManager::SetOpcode(EmuOpcode emu_op, uint16 eq_op) {
	emu_to_eq[emu_op] = eq_op;
	eq_to_emu[eq_op] = emu_op;
}


